-
Notifications
You must be signed in to change notification settings - Fork 181
RUST-2245 Implement GSSAPI auth support for Windows #1444
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Assigned |
@@ -1405,7 +1413,7 @@ functions: | |||
type: test | |||
params: | |||
binary: bash | |||
working_dir: ${PROJECT_DIRECTORY} | |||
working_dir: src |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using ${PROJECT_DIRECTORY}
on windows-64-vs2017-small
was causing "working_dir could not be found" errors. I checked the rest of config.yml
and it seems like the majority of working_dir
values are set to src
. I only chose ${PROJECT_DIRECTORY}
initially since I based this function on the aws auth functions.
@@ -235,6 +234,13 @@ features = ["serde", "serde_json-1"] | |||
rustdoc-args = ["--cfg", "docsrs"] | |||
all-features = true | |||
|
|||
# Target-specific dependencies for GSSAPI authentication |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be honest, I've never had to do this before in a Cargo.toml
file so I'm not sure if this is the idiomatic way.
let source = credential.source.as_deref().unwrap_or("$external"); | ||
|
||
#[cfg(target_os = "windows")] | ||
let (mut authenticator, initial_token) = windows::SspiAuthenticator::init( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As you see in the windows.rs
file, I created the SspiAuthenticator
struct to mirror the GssapiAuthenticator
struct. It has the same interface (although I did not go so far as to make them both implement a common trait since they'll never be compiled together). This allowed the core authentication steps in this function to remain unchanged.
@@ -0,0 +1,108 @@ | |||
use cross_krb5::{ClientCtx, InitiateFlags, K5Ctx, PendingClientCtx, Step}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file is essentially just cut-pasted from the original gssapi.rs
file. The only notable change is a minor tweak to the function signatures: they are no longer marked as async
since that is not required. I left that in basically by accident after doing all the refactoring and trial-and-erroring the first time around.
user_principal: String, | ||
} | ||
|
||
impl SspiAuthenticator { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the core implementation for the SSPI auth protocol on Windows. It is based heavily on the C implementation. I truly tried (for weeks) to get a higher level, more Rust-like library to work first. At the end of the day, though, the best way to do this was to resort to the low-level Windows API. It is a bit harder to read, but fortunately the SSPI api is not something that changes often (or will/should change in the future). The C implementation has been largely untouched for years and it still works!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm going to have to defer to @kevinAlbs to review the actual semantics of the implementation here :)
} | ||
} | ||
|
||
impl Drop for SspiAuthenticator { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Required since these handles are set and used basically exclusively in unsafe
blocks.
Is it possible to make exceptions to the |
I investigated further and am not confident SSPI support can be implemented the way our drivers require if we do not use these There are limited crate options for kerberos auth. We've ruled out Using the native Windows API inherently requires |
I haven't had the chance to look into this PR in detail, but it should work to use a |
error::{Error, Result}, | ||
}; | ||
|
||
pub(super) struct GssapiAuthenticator { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not changed in this PR, but this being a fresh file made me realize - this is only ever either
GssapiAuthenticator { pending_ctx: Some(...), established_ctx: None, is_complete: false, ... }
or
GssapiAuthenticator { pending_ctx: None, established_ctx: Some(...), is_complete: true, ... }
right? That seems like it would be better modeled via a state enum, e.g.
enum CtxState {
Pending(PendingClientCtx),
Established(ClientCtx),
}
pub(super) struct GssapiAuthenticator {
ctx: CtxState,
user_principal: String,
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah that's a nice refactor. I'll make this change.
src/client/auth/gssapi/windows.rs
Outdated
} | ||
|
||
fn acquire_credentials_and_init(&mut self, password: Option<String>) -> Result<Vec<u8>> { | ||
unsafe { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe the only unsafe portion of this is calling AcquireCredentialsHandleW
, right? I'd rather see the unsafe
blocks as narrowly scoped as possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point! I started broad to allow for whatever had to be done, but I'll narrow these down now like you suggested 👍
user_principal: String, | ||
} | ||
|
||
impl SspiAuthenticator { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm going to have to defer to @kevinAlbs to review the actual semantics of the implementation here :)
|
||
// Perform the final step of Kerberos authentication by gss_unwrap-ing the | ||
// final server challenge, then wrapping the protocol bytes + user principal. | ||
// The resulting token must be sent to the server. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is like a monad transformer 😂
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
one tiny comment, I did a pass over the C-style code but will similarly defer to @kevinAlbs on the details
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This filename should be able to stay the same - we stopped using mod.rs
files a while ago (https://doc.rust-lang.org/reference/items/modules.html#module-source-filenames)
This PR implements GSSAPI auth support for Windows as described by the spec. Similar to the Linux and Mac implementation in #1413, this one took longer than expected. In the end, it turned out that the
sspi
crate was not as amenable to this task as initially thought at scope time. I decided to attempt to implement this using the native Windows api (viawindows-sys
) and modeling it exactly off oflibmongoc
since I know the C implementation works. Of course, that results in a bit more C-style code than Rust-style code, but after several weeks of investigating and implementing, this is finally working.I updated the GSSAPI codepath to share common code between Linux/Mac and Windows as much as possible. I only separated out the platform-specific implementations and guarded each with the relevant
cfg
attributes.This PR also updates the e2e tests to run on Windows. I observed this implementation passing my local testing on a Windows host, and the e2e tests passing on this patch. I anticipate the evergreen patch associated with this PR to succeed off the bat, as well.